package com.paradise.sharding.strategy;

import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 分片策略实现
 * 包含精准分片与范围分片
 *
 * @author Paradise
 */
@Slf4j
public class RangeShardingAlgorithmImpl implements RangeShardingAlgorithm<Long>, PreciseShardingAlgorithm<Long> {

    /**
     * 100w 分片区块
     */
    private static final long DIVISOR = 1_000_000;
    /**
     * 分片表前缀
     */
    private static final String TABLE_PREFIX = "t_eth_transaction";

    public RangeShardingAlgorithmImpl() {
        log.info("初始化 -> [{}]", "RangeShardingAlgorithmImpl ----- 范围分片算法-启用");
    }

    /**
     * Sharding.
     *
     * @param availableTargetNames available data sources or tables' names
     * @param shardingValue        sharding value
     * @return sharding results for data sources or tables' names
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
        // 可用表名称 Map
        final Map<String, String> map = availableTargetNames.stream()
                .collect(Collectors.toMap(Function.identity(), Function.identity()));
        // 构造返回结果
        Collection<String> result = new LinkedHashSet<>(availableTargetNames.size());
        // 获取待查询范围
        Range<Long> range = shardingValue.getValueRange();
        // 除法取整
        long low = range.lowerEndpoint() / DIVISOR;
        long upper = range.upperEndpoint() / DIVISOR;
        // 遍历获取路由表信息
        for (long i = low; i <= upper; i++) {
            final String table = TABLE_PREFIX + i;
            if (map.containsKey(table)) {
                result.add(table);
            }
        }
        return result;
    }

    /**
     * Sharding.
     *
     * @param availableTargetNames available data sources or tables' names
     * @param shardingValue        sharding value
     * @return sharding result for data source or table's name
     */
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
        // 可用表名称 Map
        final Map<String, String> map = availableTargetNames.stream()
                .collect(Collectors.toMap(Function.identity(), Function.identity()));
        final long value = shardingValue.getValue() / DIVISOR;
        final String table = TABLE_PREFIX + value;
        if (map.containsKey(table)) {
            return table;
        }
        log.info("分片表：{} 不存在", table);
        throw new UnsupportedOperationException(table + " Not Exist!");
    }

}
